package zdream.pmfield.test.stage.effect;

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;

import zdream.pmfield.data.foe.BStat;
import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.stage.StageBuilderForTest;
import zdream.pmfield.stage.StageWrapper;
import zdream.pmfield.stage.message.ActionStartMessage;
import zdream.pmfield.stage.message.AttackMessage;
import zdream.pmfield.stage.message.FoeAddBuffMessage;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.message.TurnFinishedMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.storage.FoeTemplateStorage;
import zdream.pmfield.test.utils.ResourceConstant;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-12-05
 * @since 0.0.1
 */
public class FlinchBuffCase {
	FoeTemplateStorage storage;

	@Before
	public void before() {
		storage = new FoeTemplateStorage();
		storage.load();
		storage.loadMovesInResource(ResourceConstant.RES_PATH_TEST_MOVES_JSON);
	}

	@Test
	public void testFlinchEffect() {
		MoveTemplate flinchMove = storage.findMoveById(10000024, 1);
		assertEquals("肯定害怕", flinchMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);

		Foe foe1 = Foe.builder(storage.findFoeById(4))
				.withLevel(30)
				.withMove(flinchMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(1))
				.withLevel(30)
				.withMove(normalMove)
				.skipShuffleVStat()
				.build();
		assertTrue(foe1.bStats[BStat.SP.ordinal()] > foe2.bStats[BStat.SP.ordinal()]);

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		System.out.println("TEST 1: apply flinch");
		AtomicBoolean flinchRequest = new AtomicBoolean(false);
		AtomicBoolean flinchApplied = new AtomicBoolean(false);
		AtomicBoolean checked = new AtomicBoolean(false);

		wrapper.printBeforeHandleMessage();
		wrapper.onActionRequestForAll(wrapper.createDefaultController().withActionChecker(action -> {
			if (action.move.move == flinchMove) {
				flinchRequest.set(true);
			}
		}));
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			assertTrue(message.buff.is(StageConstant.EFFECT_FLINCH));
			assertEquals(foe2, message.buff.target.foe);
			assertTrue(message.success);
			flinchApplied.set(true);
		});
		wrapper.onMessage(TurnFinishedMessage.class, message -> {
			if (!flinchRequest.get()) {
				return;
			}
			assertTrue(flinchApplied.get());
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());
		wrapper.runUntil(TurnFinishedMessage.class);

		System.out.println("TEST 2: flinch 状态: 跳过回合, 并删除状态, 增加临时免疫");
		// 现在 foe2 眩晕了

		checked.set(false);
		AtomicBoolean foe2TurnStarted = new AtomicBoolean(false);
		AtomicBoolean foe2ActionStarted = new AtomicBoolean(false);
		AtomicBoolean foe2FlinchRemoved = new AtomicBoolean(false);
		AtomicBoolean foe2FlinchImmunityAdded = new AtomicBoolean(false);

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.onMessage(TurnStartMessage.class, message -> {
			final FoeActor foe = message.foe;
			if (foe.foe != foe2) {
				return;
			}
			foe2TurnStarted.set(true);
		});
		wrapper.onMessage(FoeRemoveBuffMessage.class, message -> {
			if (!foe2TurnStarted.get()) {
				return;
			}
			assertTrue(message.buff.is(StageConstant.BUFF_FLINCH));
			foe2FlinchRemoved.set(true);
		});
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			if (!foe2FlinchRemoved.get()) {
				return;
			}
			assertTrue(message.buff.is(StageConstant.BUFF_FLINCH_TEMP_IMMUNITY));
			foe2FlinchImmunityAdded.set(true);
		});
		wrapper.onMessage(ActionStartMessage.class, message -> {
			if (!foe2FlinchImmunityAdded.get()) {
				return;
			}
			assertTrue(message.action.isForDaze());
			assertEquals(foe2, message.action.asDaze().foe.foe);
			foe2ActionStarted.set(true);
		});
		wrapper.onMessage(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertNotEquals(foe2, message.report.action.asAttack().foe.foe);
		});
		wrapper.onMessage(TurnFinishedMessage.class, message -> {
			if (!foe2ActionStarted.get()) {
				return;
			}
			assertEquals(foe2, message.foe.foe);
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 3: flinch 临时免疫状态: 无法被施加 flinch");
		// 现在 foe2 免疫眩晕了
		// 因为前面比过速度, 按道理来说, 上一个是 foe2, 下一个应该是 foe1

		checked.set(false);
		wrapper.clearOnMessageHooks();
		wrapper.onMessage(TurnStartMessage.class,
				message -> assertEquals(foe1, message.foe.foe));
		wrapper.onActionRequestForAll(wrapper.createDefaultController().withActionChecker(action -> {
				assertEquals(flinchMove, action.move.move);
		}));
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
				assertEquals(foe2, message.foe.foe);
				assertTrue(message.buff.is(StageConstant.BUFF_FLINCH));
				assertFalse(message.success);
				assertTrue(message.rejectBy.is(StageConstant.BUFF_FLINCH_TEMP_IMMUNITY));
				checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 4: flinch 临时免疫状态只持续一轮");

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);

		foe2FlinchRemoved.set(false);
		wrapper.onMessage(FoeRemoveBuffMessage.class, message -> {
			if (!foe2TurnStarted.get()) {
				return;
			}
			assertTrue(message.buff.is(StageConstant.BUFF_FLINCH_TEMP_IMMUNITY));
			foe2FlinchRemoved.set(true);
		});
		wrapper.runUntil(TurnFinishedMessage.class);

		assertTrue(foe2FlinchRemoved.get());
	}
}
